package com.zshy.base.idempotent.aspect;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.zshy.base.cache.utils.RedisKit;
import com.zshy.base.core.context.ThreadContext;
import com.zshy.base.core.contract.enums.ResultCodeEnum;
import com.zshy.base.core.contract.exception.ServiceException;
import com.zshy.base.core.utils.JsonKit;
import com.zshy.base.idempotent.annotation.Idempotent;
import com.zshy.base.kit.servlet.ServletKit;
import com.zshy.base.protocol.contract.dto.HeaderDTO;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;

/**
 * 防止重复提交
 *
 * @author zhouhengzhe
 */
@Aspect
public class IdempotentAspect {

    @Before("@annotation(idempotent)")
    public void doBefore(JoinPoint point, Idempotent idempotent) {
        // 如果注解不为0 则使用注解数值
        long interval = idempotent.timeUnit().toMillis(idempotent.interval());

        if (interval < 1000) {
            throw new ServiceException(ResultCodeEnum.REPEAT_SUBMIT_INTERVAL_NOT_LESS_THAN_1_SECOND);
        }
        HttpServletRequest request = ServletKit.getRequest();
        String param = argsArrayToString(point);

        // 请求地址（作为存放cache的key值）
        String url = request.getRequestURI();
        HeaderDTO headerDTO = HeaderDTO.parseForRequestHeader(request);
        // 唯一值（没有消息头则使用请求地址）
        String submitKey = StringUtils.trimToEmpty(headerDTO.getRequestId());

        submitKey = SecureUtil.md5(submitKey + ":" + param);

        // 唯一标识（指定key + url + 消息头）
        String cacheRepeatKey = "global:repeat_submit:" + url + submitKey;
        if (RedisKit.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) {
            ThreadContext.set(cacheRepeatKey, cacheRepeatKey);
        } else {
            String message = idempotent.message();
            if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
                //message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
            }
            throw new ServiceException(500, message);
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(JoinPoint point) {
        StringJoiner params = new StringJoiner(" ");
        if (ArrayUtil.isEmpty(point.getArgs())) {
            return params.toString();
        }
        for (Object o : point.getArgs()) {
            if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                params.add(JsonKit.toJson(o));
            }
        }
        return params.toString();
    }


    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.values()) {
                return value instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
